﻿using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using Microsoft.Office.Project.EPMAuditing.AuditListenerCatalog;
using Microsoft.Office.Project.EPMAuditing.Library;
using System.Globalization;

namespace Microsoft.Office.Project.EPMAuditing.AuditListenerCatalog
{
    public class AuditListenerCollection : IEnumerable, IDisposable
    {
        private Collection<AuditListener> _listeners;

        public AuditListenerCollection() {}

        public AuditListenerCollection(Collection<Listener> listenerCollection, Tracing tracing)
        {
            UpdateAuditListeners(listenerCollection, tracing);
        }

        // Use C# destructor syntax for finalization code.
        ~AuditListenerCollection()
        {
            // Simply call Dispose(false).
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Free other state (managed objects).
                CloseOpenListeners();
            }
            // Free your own state (unmanaged objects).
            // Set large fields to null.
        }

        /// <summary>
        /// Implemented to allow enumeration of the collection
        /// </summary>
        /// <returns>IEnumerator which allows enumeration of the collection.</returns>
        public IEnumerator GetEnumerator()
        {
            return _listeners.GetEnumerator();
        }

        public void UpdateAuditListeners(Collection<Listener> listenerCollection, Tracing tracing)
        {
            if (_listeners != null)
                CloseOpenListeners();
            else
                _listeners = new Collection<AuditListener>();

            // TODO: Add a trace statement to the final class
            if (listenerCollection == null)
                return;

            string fullPath = Assembly.GetExecutingAssembly().Location;
            string assemblyDir = fullPath.Substring(0, fullPath.LastIndexOf(@"\", StringComparison.OrdinalIgnoreCase) + 1);

            foreach (Listener listener in listenerCollection)
            {
                if (listener.Enabled)
                {
                    //Load the Assembly
                    Assembly a = Assembly.LoadFrom(string.Format(CultureInfo.InvariantCulture, "{0}{1}",assemblyDir,listener.DLL));

                    // get the specific type from the loaded assembly
                    Type auditType = a.GetType(listener.ClassName);

                    if (auditType != null)
                    {
                        if (DerivedFromAuditListener(auditType) && !auditType.IsAbstract)
                        {
                            // dynamically create or activate(if exist) object
                            object obj = Activator.CreateInstance(auditType, ArgCollectionToObjectArray(listener.Args));

                            AuditListener objListener = (AuditListener)obj;
                            _listeners.Add(objListener);
                            objListener.TracingService = tracing;
                        }
                    }
                }
            }

        }

        private static object[] ArgCollectionToObjectArray(Collection<Arg> argCollection)
        {
            if (argCollection.Count > 0)
            {
                Collection<object> objectCollection = new Collection<object>();
                foreach (Arg argument in argCollection)
                {
                    objectCollection.Add(argument.Value);
                }

                return objectCollection.ToArray();
            }
            return null;
        }

        private void CloseOpenListeners()
        {
            if (_listeners != null)
            {
                foreach (AuditListener listener in _listeners)
                {
                    // Dispose on the listener before clearing the list
                    listener.Dispose();
                }
                _listeners.Clear();
            }
        }

        private bool DerivedFromAuditListener(Type typ)
        {
            if (typ.BaseType.Name == "AuditListener")
                return true;
            else if (typ.BaseType.Name != "Object")
                return DerivedFromAuditListener(typ.BaseType);
            else
                return false;
        }

    }
}
